package db

import (
	"context"
	"demo/infra/errors"
	"fmt"
	"gorm.io/gorm"
)

// TxFunc 定义事务中执行的业务逻辑函数
// 参数：ctx 上下文，tx 事务对象
// 返回值：error 若返回非nil，则事务回滚；nil则提交
type TxFunc func(ctx context.Context, tx *gorm.DB) error

// WithTransactionAndPanic 通用事务封装
// 参数：db 数据库连接，ctx 上下文，fn 业务逻辑函数
func WithTransactionAndPanic(db *gorm.DB, ctx context.Context, fn TxFunc) {
	err := withTransaction(db, ctx, fn)
	if err != nil {
		// 如果已经是BizError，直接返回；否则包装为BizError
		if bizErr, ok := err.(*errors.BizError); ok {
			panic(bizErr)
		}
		panic(errors.NewBizError("B0000", err.Error()))
	}
}

// withTransaction 通用事务封装
// 参数：db 数据库连接，ctx 上下文，fn 业务逻辑函数
// 返回值：error 事务执行过程中的错误
func withTransaction(db *gorm.DB, ctx context.Context, fn TxFunc) error {
	// 开启事务
	tx := db.WithContext(ctx).Begin()
	if tx.Error != nil {
		return errors.NewBizError("B0000", fmt.Sprintf("事务开启失败: %v", tx.Error))
	}
	defer func() {
		// 处理panic，确保事务回滚
		if r := recover(); r != nil {
			tx.Rollback()
			// 可以在这里记录panic日志
			//log.Error(ctx, "事务panic后回滚失败", "error", r)
		}
	}()
	// 执行业务逻辑
	if err := fn(ctx, tx); err != nil {
		// 业务逻辑出错，回滚事务
		if rollbackErr := tx.Rollback().Error; rollbackErr != nil {
			return errors.NewBizError("B0000", fmt.Sprintf("事务回滚失败: %v, 原始错误: %v", rollbackErr, err))
		}
		// 如果已经是BizError，直接返回；否则包装为BizError
		if bizErr, ok := err.(*errors.BizError); ok {
			return bizErr
		}
		return errors.NewBizError("B0000", err.Error())
	}
	// 业务逻辑执行成功，提交事务
	if err := tx.Commit().Error; err != nil {
		// 提交失败 → 转换为BizError
		return errors.NewBizError("B0000", fmt.Sprintf("事务提交失败: %v", err))
	}
	return nil
}
